#include "gm3d.h"

int main(int argc, char* argv[])
{
	gctl::flags_parser fp;
	fp.set_proname(argv[0]);
	fp.set_proinfo("3D model construction, forward and invert modeling of gravity and magnetic data under the Cartesian coordinates.");

	fp.add_opt('i', "input-mesh", required_argument, NULL, "Input Gmsh(.msh) model file for forward calculation.", "<file>", false);
	fp.add_opt('o', "output-mesh", required_argument, NULL, "Output Gmsh(.msh) model file for model construction.", "<file>", false);
	fp.add_opt('a', "anomalies", required_argument, NULL, "Output anomalies of the gravity or magnetic data.", "<file>", false);
	fp.add_opt('d', "dimension", required_argument, NULL, "3D dimensions of the model space. the suffix 's' means the starting coordinate \
and 'e' represents the ending coordinate in axial directions. 'dx', 'dy' and 'dz' are step lengths. The default value is 10/20/990/10/20/990/10/20/490. \
The axial orientation adopted by the program is a right-hand Cartesian system with the z-axis point vertical downward.", 
	"<xs>/<dx>/<xe>/<ys>/<dy>/<ye>/<zs>/<dz>/<ze>", false);
	fp.add_opt('m', "model", required_argument, NULL, "Model file that contains model parameters. See instructions for formats of different model types. \
Note that any line starts with '#' will be skipped. Four different types of models are supported by the program. Keyword parameters for different model types are shown as: \
1. regular_block add|replace|erase <physical_value> <xmin>/<xmax>/<ymin>/<ymax>/<zmin>/<zmax> 2. tilted_block add|replace|erase <physical_value> <xmin_z>/<xmax_z>/<ymin_z>/<ymax_z>/<zmin>/<xmin_Z>/<xmax_Z>/<ymin_Z>/<ymax_Z>/<zmax> \
3. sphere add|replace|erase <physical_value> <xcen>/<ycen>/<zcen>/<xradius>/<yradius>/<zradius> 4. interface add|replace|erase/top|bot <physical_value> <filename>", 
	"<file>", false);
	fp.add_opt('e', "element", required_argument, NULL, "Element data name of the input/output Gmsh(.msh) file. Note that the name must be around by \"\".", 
	"<name>", false);
	fp.add_opt('p', "position", required_argument, NULL, "Observation positions. You can either initialize the observation points from parameters or a file. \
Each line of the file contain coordinates y(easting), x(northing) and z(elevation) of an observation point.", 
	"<observation-file>|<xs>/<dx>/<xe>/<ys>/<dy>/<ye>/<elevation>", false);
	fp.add_opt('t', "type", required_argument, NULL, "Forward component Vz, Vzx, Vzy or Vzz for gravitational data and \
DeltaT, DeltaTx, DeltaTy, DeltaTz, Hax, Hay and Za for magnetic data.", 
	"Vz|Vzx|Vzy|Vzz|DT|DTx|DTy|DTz|Hax|Hay|Za", false);
	fp.add_opt('v', "vector", required_argument, NULL, "Inclination and declination of the geomagnetic field and magnetization.", "<I0>/<D0>/<I>/<D>", false);
	fp.add_opt('n', "noise", required_argument, NULL, "Add noise to the forward calculated data", "<mean>/<std>", false);
	fp.add_opt('r', "remove", no_argument, NULL, "Remove model elements with no data in the output Gmsh(.msh) file.", 0, false);
	fp.add_opt('l', "log", required_argument, NULL, "Log file. A default name will be used if this option is not set.", "<file>", false);
	fp.add_opt('h', "help", no_argument, NULL, "Show help information.", 0, false);
	fp.configure(argc, argv);

	try
	{
		if(argc == 1 || fp.set_opt('h'))
		{
			fp.show_help_page();
			return 0;
		}

		bool build_model = true;
		bool remove_null = false;
		std::string modelname, in_mshname, out_mshname, elename, obsername, out_obserfile;
		std::string noise_para, forward_type, mag_field_para, dimension, logfile_name;

		fp.get_argv({'m', 'i', 'o', 'e', 'p', 'a', 'n', 't', 'v', 'd', 'l'}, 
			{&modelname, &in_mshname, &out_mshname, &elename, &obsername, &out_obserfile, 
			&noise_para, &forward_type, &mag_field_para, &dimension, &logfile_name});
		if (logfile_name == "NULL") logfile_name = "gm3d.history.txt";
		if (in_mshname != "NULL") build_model = false;
		if (fp.set_opt('r')) remove_null = true;

		// 查看是否通过强制参数检查
		if (!fp.pass_mandatory()) return 0;

		GM3D gm3d_instance;
		if (build_model)
		{
			gm3d_instance.BuildRegularGrid(dimension.c_str());
			if (modelname != "NULL") gm3d_instance.AddModels(modelname.c_str());
			else
			{
				GCTL_ShowWhatError("No model parameters is found.", GCTL_WARNING_ERROR, 0, 0, 0);
			}
			gm3d_instance.RegisteredOuput(remove_null);
			gm3d_instance.OutMshFile(out_mshname.c_str(), elename);
		}
		else
		{
			gm3d_instance.ReadModel(in_mshname.c_str(),elename.c_str());
			gm3d_instance.InitObs(obsername.c_str());

			if (forward_type == "Vz") gm3d_instance.ForwardVz(noise_para.c_str());
			else if (forward_type == "Vzx") gm3d_instance.ForwardVzx(noise_para.c_str());
			else if (forward_type == "Vzy") gm3d_instance.ForwardVzy(noise_para.c_str());
			else if (forward_type == "Vzz") gm3d_instance.ForwardVzz(noise_para.c_str());
			else if (forward_type == "DT") gm3d_instance.ForwardDeltaT(noise_para.c_str(),mag_field_para.c_str());
			else if (forward_type == "DTx") gm3d_instance.ForwardDeltaTx(noise_para.c_str(),mag_field_para.c_str());
			else if (forward_type == "DTy") gm3d_instance.ForwardDeltaTy(noise_para.c_str(),mag_field_para.c_str());
			else if (forward_type == "DTz") gm3d_instance.ForwardDeltaTz(noise_para.c_str(),mag_field_para.c_str());
			else if (forward_type == "Hax") gm3d_instance.ForwardHax(noise_para.c_str(),mag_field_para.c_str());
			else if (forward_type == "Hay") gm3d_instance.ForwardHay(noise_para.c_str(),mag_field_para.c_str());
			else if (forward_type == "Za") gm3d_instance.ForwardZa(noise_para.c_str(),mag_field_para.c_str());
			else throw "wrong forward component type.";

			gm3d_instance.OutObs(out_obserfile.c_str());
		}

		//最后输出一个记录文件 我们使用唯一的输出文件名称 所以每次在统一目录运行都会覆盖
		ofstream log_out;
		gctl::open_outfile(log_out, logfile_name);

		time_t now = time(0);
		char* dt = ctime(&now);

		log_out << "Time: " << dt;
		log_out << "Mesh: " << dimension << endl;
		log_out << "Model id: " << gm3d_instance.model_id_ << endl;
		log_out << "Input .msh file: " << in_mshname << endl;
		log_out << "Output .msh file: " << out_mshname << endl;
		log_out << "Model list file: " << modelname << endl;
		log_out << "Model element name: " << elename << endl;
		log_out << "observation parameters: " << obsername << endl;
		log_out << "Output observation file: " << out_obserfile << endl;
		log_out << "noise level: " << noise_para << endl;
		log_out << "forward type: " << forward_type << endl;
		log_out << "magnetic field parameters: " << mag_field_para << endl;
		log_out.close();
	}
	catch(std::string err_str)
	{
		GCTL_ShowWhatError(err_str, GCTL_ERROR_ERROR, 0, 0, 0);
	}
	catch(const char* err_char)
	{
		GCTL_ShowWhatError(err_char, GCTL_ERROR_ERROR, 0, 0, 0);
	}
	return 0;
}
